from data import Lattice, Catalogue
from utils import plotting
import matplotlib.pyplot as plt
from tqdm import tqdm
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly
plotly.offline.init_notebook_mode()
Import unit cell catalogue from PNAS paper by Lumpe, T. S. and Stankovic, T. (2020)
https://www.pnas.org/doi/10.1073/pnas.2003504118
Catalogue can be downloaded from
https://doi.org/10.3929/ethz-b-000457598
cat = Catalogue.from_file('./Unit_Cell_Catalog.txt', indexing=1)
print(cat)
Unit cell catalogue with 17222 entries
Plot a few lattices
names = cat.names
for i in [0,2]:
lat = Lattice(**cat.get_unit_cell(names[i]))
fig = plotting.plotly_unit_cell_3d(lat)
fig.show()
Lattice ort_Z08.0_E2 below is an example of a faulty unit cell. Beams between nodes 0-3 and 1-2 are intersecting, but no node exists at the intersection
names = cat.names
lat = Lattice(**cat.get_unit_cell(names[1]))
fig = plotting.plotly_unit_cell_3d(lat)
fig.show()
Collect some statistics about unit cells
df_data = {}
for name in tqdm(cat.names):
lat = Lattice(**cat.get_unit_cell(name))
lat.calculate_edge_lengths(repr='transformed')
# extract minimum distance between any two nodes
pos = lat.transformed_node_coordinates
matrix = pos.reshape((lat.num_nodes, 1, 3)) - pos.reshape((1, lat.num_nodes, 3))
distances = np.sqrt(np.sum(matrix**2, axis=2))
distances = distances[np.triu_indices_from(distances, k=1)]
props = {
'num_nodes':lat.num_nodes,
'num_edges':lat.num_edges,
'min_edge_length':lat.transformed_edge_lengths.min(),
'mean_edge_length':lat.transformed_edge_lengths.mean(),
'max_edge_length':lat.transformed_edge_lengths.max(),
'min_nodal_distance':distances.min()
}
df_data.update({name:props})
df = pd.DataFrame(df_data).T
df
100%|██████████| 17222/17222 [00:13<00:00, 1230.69it/s]
| num_nodes | num_edges | min_edge_length | mean_edge_length | max_edge_length | min_nodal_distance | |
|---|---|---|---|---|---|---|
| cub_Z06.0_E1 | 8.0 | 12.0 | 1.000000 | 1.000000 | 1.000000 | 1.000000 |
| ort_Z08.0_E2 | 8.0 | 12.0 | 0.850750 | 0.959102 | 1.026555 | 0.574490 |
| cub_Z08.0_E3 | 9.0 | 8.0 | 0.866025 | 0.866025 | 0.866025 | 0.866025 |
| hex_Z08.0_E4 | 8.0 | 14.0 | 0.994710 | 0.996221 | 1.000000 | 0.994710 |
| tet_Z04.0_E5 | 12.0 | 14.0 | 0.500000 | 0.501286 | 0.503000 | 0.500000 |
| ... | ... | ... | ... | ... | ... | ... |
| tet_Z03.7_R2726 | 168.0 | 212.0 | 0.025296 | 0.071671 | 0.086391 | 0.025296 |
| cub_Z03.5_R2727 | 96.0 | 132.0 | 0.209300 | 0.210274 | 0.211071 | 0.201681 |
| cub_Z05.0_R2728 | 60.0 | 114.0 | 0.204120 | 0.249877 | 0.271540 | 0.204120 |
| mon_Z05.7_R2729 | 34.0 | 45.0 | 0.000765 | 0.185892 | 0.241351 | 0.000765 |
| cub_Z04.0_R2730 | 1160.0 | 2026.0 | 0.043030 | 0.084368 | 0.086736 | 0.043030 |
17222 rows × 6 columns
df.describe()
| num_nodes | num_edges | min_edge_length | mean_edge_length | max_edge_length | min_nodal_distance | |
|---|---|---|---|---|---|---|
| count | 17222.000000 | 17222.000000 | 17222.000000 | 17222.000000 | 17222.000000 | 17222.000000 |
| mean | 91.254268 | 113.180989 | 0.108358 | 0.234239 | 0.336314 | 0.092567 |
| std | 101.660985 | 150.516632 | 0.109203 | 0.110778 | 0.185275 | 0.098855 |
| min | 6.000000 | 6.000000 | 0.000003 | 0.031022 | 0.039054 | 0.000000 |
| 25% | 40.000000 | 49.000000 | 0.024573 | 0.159061 | 0.204124 | 0.019207 |
| 50% | 66.000000 | 80.000000 | 0.079647 | 0.215477 | 0.293243 | 0.065931 |
| 75% | 112.000000 | 132.000000 | 0.153460 | 0.284022 | 0.420058 | 0.131330 |
| max | 4224.000000 | 7008.000000 | 1.000000 | 1.091532 | 1.410062 | 1.000000 |
Most unit cells have below 200 nodes and edges, but the most complex lattices have thousands. Note that another class of faulty lattices are those which have overlapping nodes -- minimum distance between unique nodes is 0.
fig = make_subplots(
rows=2, cols=2,
subplot_titles=("Number of nodes", "Number of edges", "Mean edge lengths", "Minimum nodal distance")
)
marker_dict = {'line':{'color':'black', 'width':0.5}}
fig.add_histogram(
x=df['num_nodes'], name='Nodes', row=1, col=1,
marker=marker_dict
)
fig.add_histogram(
x=df['num_edges'], name='Edges', row=1, col=2,
marker=marker_dict,
)
fig.add_histogram(
x=df['mean_edge_length'], name='Mean edge length', row=2, col=1,
marker=marker_dict,
)
fig.add_histogram(
x=df['min_nodal_distance'], name='Minimum distance between nodes', row=2, col=2,
marker=marker_dict,
)
fig.update_layout(xaxis_range=[0,1000])
fig.update_layout(xaxis2_range=[0,1000])
fig.update_layout(title='Unit cell statistics')
fig.update_layout(height=800, width=800, showlegend=False)
fig
Plot unit cells with most nodes/edges. Note that plotly visualisation takes a while
print(df[df.num_nodes==df.num_nodes.max()])
num_nodes num_edges min_edge_length mean_edge_length \
cub_Z04.0_R2526 4224.0 7008.0 0.000636 0.043935
max_edge_length min_nodal_distance
cub_Z04.0_R2526 0.047514 0.000636
lat = Lattice(**cat.get_unit_cell('cub_Z04.0_R2526'))
fig = plotting.plotly_unit_cell_3d(lat, node_numbers=False)
fig.show()
Plot a unit cell with overlapping nodes
print(df.sort_values(by='min_nodal_distance').head(10))
num_nodes num_edges min_edge_length mean_edge_length \
trig_Z05.0_E6999 136.0 143.0 0.000099 0.060219
trig_Z05.9_E6124 113.0 132.0 0.028911 0.098573
trig_Z04.8_E6128 128.0 132.0 0.002935 0.061820
trig_Z05.1_E6998 132.0 143.0 0.022077 0.072384
trig_Z04.5_E7306 103.0 118.0 0.000003 0.073825
trig_Z04.4_E9350 116.0 150.0 0.000003 0.096554
ort_Z05.2_E3240 44.0 56.0 0.000003 0.134318
ort_Z05.0_E3485 46.0 59.0 0.000003 0.141029
hex_Z03.5_R2193 124.0 152.0 0.000004 0.066356
trig_Z05.3_E7254 80.0 121.0 0.000004 0.124587
max_edge_length min_nodal_distance
trig_Z05.0_E6999 0.119700 0.000000
trig_Z05.9_E6124 0.190920 0.000000
trig_Z04.8_E6128 0.112510 0.000001
trig_Z05.1_E6998 0.132310 0.000001
trig_Z04.5_E7306 0.094420 0.000003
trig_Z04.4_E9350 0.112409 0.000003
ort_Z05.2_E3240 0.172888 0.000003
ort_Z05.0_E3485 0.174143 0.000003
hex_Z03.5_R2193 0.080096 0.000004
trig_Z05.3_E7254 0.136151 0.000004
Plot unit cell ort_Z05.2_E3240 which has a relatively low number of nodes. Observing the overlapping node numbers, we see the nodes with overlapping coordinates.
lat = Lattice(**cat.get_unit_cell('ort_Z05.2_E3240'))
fig = plotting.plotly_unit_cell_3d(lat, repr='transformed')
fig.show()